﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Blog.Types.Repository;
using System.Data.Linq;

namespace Blog.DbRepository
{
    public class DbRepository : IRepository
    {
        public string BlogConnectionString 
        {
            get
            {
                return ContextFactory.BlogConnectionString;
            }
            set
            {
                ContextFactory.BlogConnectionString = value;
            }
        }

        public string ApplicationConnectionString
        {
            get
            {
                return ContextFactory.ApplicationConnectionString;
            }
            set
            {
                ContextFactory.ApplicationConnectionString = value;
            }
        }

        public List<ITheme> GetThemes()
        {
            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                {
                    return  (from t in db.Themes
                             orderby t.ID descending
                             select t).OfType<ITheme>().ToList();
                }
            }
            catch (Exception exc)
            {
                throw new BlogException(exc).ReThrowException();
            }
        }
        
        public ITheme GetTheme(int objId)
        {
            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                {
                    return (from t in db.Themes
                           where t.ID == objId
                           select t).SingleOrDefault();
                }
            }
            catch (Exception exc)
            {
                throw new BlogException(exc).ReThrowException();
            }
        }

        public List<ITheme> FindThemes(int startRowIndex, int pageSize, string text, bool findHeaders, bool findContext, out int endRowIndex)
        {
            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                {
                    text = text.ToLower();

                    var finder = from t in db.Themes
                            where (findHeaders && t.ThemeHeader.ToLower().IndexOf(text) >= 0) || 
                                  (findContext && t.ThemeDescription.ToLower().IndexOf(text) >= 0)
                            select t;

                    List<ITheme> result = finder.Skip(startRowIndex).Take(pageSize).OfType<ITheme>().ToList();
                    endRowIndex = finder.Count();
                    return result;
                }
            }
            catch (Exception exc)
            {
                throw new BlogException(exc).ReThrowException();
            }
        }

        public int Save(int idTheme, Action<ITheme> setData)
        {
            if (setData == null)
                throw new ArgumentNullException("setData");

            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                using (System.Transactions.TransactionScope transaction = new System.Transactions.TransactionScope())
                {
                    Theme dbTheme = idTheme == 0 ? new Theme() : (from a in db.Themes
                                                                  where a.ID == idTheme
                                                                    select a).FirstOrDefault();
                    setData(dbTheme);
                    
                    if (idTheme == 0)
                        db.Themes.InsertOnSubmit(dbTheme);                    

                    db.SubmitChanges(ConflictMode.FailOnFirstConflict);

                    transaction.Complete();
                    return dbTheme.ID;
                }
            }
            catch (Exception exc)
            {
                if (exc is BlogException)
                    throw exc;

                throw new BlogException(exc).ReThrowException();
            }
        }

        public void DeleteTheme(int objId)
        {
            if (objId <= 0)
                return;

            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                using (System.Transactions.TransactionScope transaction = new System.Transactions.TransactionScope())
                {
                    var trees = from tree in db.MessageTrees
                              where tree.ThemeId == objId                              
                              select tree;

                    var mess = from mes in db.Messages
                                 where trees.FirstOrDefault(t => t.TreeID == mes.MessageTreeId) != null
                                 select mes;

                    foreach (var m in mess)
                        db.Messages.DeleteOnSubmit(m);

                    foreach (var t in trees)
                        db.MessageTrees.DeleteOnSubmit(t);

                    var theme = (from a in db.Themes
                                where a.ID == objId
                                select a).FirstOrDefault();

                    db.Themes.DeleteOnSubmit(theme);

                    db.SubmitChanges(ConflictMode.FailOnFirstConflict);

                    transaction.Complete();
                }
            }
            catch (Exception exc)
            {
                throw new BlogException(exc).ReThrowException();
            }
        }

        public List<IMessage> GetMessages(int idTheme)
        {
            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                {
                    var res = from tree in db.MessageTrees
                              where tree.ThemeId == idTheme
                              orderby tree.ModifyDateTime descending
                              join mess in db.Messages
                              on tree.TreeID equals mess.MessageTreeId into messJoin
                              from message in messJoin
                              select message;

                    return res == null ? new List<IMessage>() : res.OfType<IMessage>().ToList();
                }
            }
            catch (Exception exc)
            {
                throw new BlogException(exc).ReThrowException();
            }
        }

        public int Save(int idTheme, int idMessage, Action<IMessage> setData, out int messageTreeId)
        {
            if (idTheme == 0)
                throw new ArgumentException("idTheme");
            if (setData == null)
                throw new ArgumentNullException("setData");

            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                using (System.Transactions.TransactionScope transaction = new System.Transactions.TransactionScope())
                {
                    Message dbMessage = idMessage == 0 ? new Message() : (from a in db.Messages
                                                                          where a.MessageId == idMessage
                                                                          select a).FirstOrDefault();
                    setData(dbMessage);

                    MessageTree dbMessageTree = dbMessage.MessageTreeId == 0 ? new MessageTree() : (from a in db.MessageTrees
                                                                                                    where a.TreeID == dbMessage.MessageTreeId
                                                                                                    select a).FirstOrDefault();

                    dbMessage.ModifyDateTime = DateTime.Now;
                    dbMessageTree.ModifyDateTime = DateTime.Now;

                    if (dbMessageTree.TreeID == 0)
                    {
                        dbMessageTree.ThemeId = idTheme;
                        db.MessageTrees.InsertOnSubmit(dbMessageTree);
                    }

                    if (dbMessage.MessageId == 0)                    
                        db.Messages.InsertOnSubmit(dbMessage);

                    dbMessage.MessageTree = dbMessageTree;

                    db.SubmitChanges(ConflictMode.FailOnFirstConflict);

                    transaction.Complete();

                    messageTreeId = dbMessageTree.TreeID;
                    return dbMessage.MessageId;
                }
            }
            catch (Exception exc)
            {
                if (exc is BlogException)
                    throw exc;

                throw new BlogException(exc).ReThrowException();
            }
        }

        public void DeleteMessage(int objId)
        {
            if (objId <= 0)
                return;

            try
            {
                using (DbDataContext db = ContextFactory.CreateDataContext())
                using (System.Transactions.TransactionScope transaction = new System.Transactions.TransactionScope())
                {
                    bool hasChildren = (from a in db.Messages
                                        where a.ParentId == objId
                                        select a).Count() != 0;

                    if (hasChildren)
                        throw new BlogException("Нельзя удалить сообщение, т.к. уже есть ответы");

                    db.Messages.DeleteOnSubmit((from a in db.Messages
                                              where a.MessageId == objId
                                              select a).FirstOrDefault());

                    db.SubmitChanges(ConflictMode.FailOnFirstConflict);

                    transaction.Complete();
                }
            }
            catch (Exception exc)
            {
                if (exc is BlogException)
                    throw exc;

                throw new BlogException(exc).ReThrowException();
            }
        }
    }
}
